@file:Suppress("SimpleDateFormat")
package kotlinx.coroutines.script

import android.graphics.Color
import androidx.lifecycle.MutableLiveData
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.script.tes.CrashUtils
import kotlinx.coroutines.script.tes.DiskLogUtils
import kotlinx.coroutines.script.tes.Keys
import kotlinx.coroutines.script.tes.ServerApi
import kotlinx.coroutines.script.tes.analysisEvent
import kotlinx.coroutines.script.tes.appScopeLaunch
import kotlinx.coroutines.script.tes.printLog
import kotlinx.coroutines.script.tes.runOnUIThread
import kotlinx.coroutines.script.tes.shake
import kotlinx.coroutines.script.tes.toast
import kotlinx.coroutines.script.tes.writeToDisk
import sample.ui.ConsoleLayout.Companion.outputText
import sample.ui.FloatControllerLayout
import sample.ui.Properties
import sample.ui.SmallMapLayout
import v9.assist.access.releaseLastGestures
import v9.assist.script.errorEndLogout
import v9.assist.script.startSceneFB
import v9.assist.script.startSceneFM
import v9.assist.script.startSceneFW
import v9.assist.script.startSceneHM
import v9.assist.script.startSceneHY
import v9.assist.script.startSceneJL
import v9.assist.script.startSceneJT
import v9.assist.script.startSceneRH
import v9.assist.script.startSceneSM
import v9.assist.script.startSceneYW
import v9.assist.script.stopSceneFB
import v9.assist.script.stopSceneFM
import v9.assist.script.stopSceneFW
import v9.assist.script.stopSceneHM
import v9.assist.script.stopSceneHY
import v9.assist.script.stopSceneJL
import v9.assist.script.stopSceneJT
import v9.assist.script.stopSceneRH
import v9.assist.script.stopSceneSM
import v9.assist.script.stopSceneYW
import v9.assist.script.xLastUnknownTs
import v9.assist.script.xScriptEndTs
import v9.assist.script.xScriptStartTs

class Controller {
    private var jobHeartBeat: Job? = null
    private var jobStart: Job? = null
    private var jobCancel: Job? = null
    private var jobCheck: Job? = null

    fun start() = runOnUIThread { // uiThread
        if (isRunning) return@runOnUIThread
        printLog { "Controller start" }
        clickStartStatus.value = true
        running.value = true
        jobCancel?.cancel()
        jobCheck = appScopeLaunch(Dispatchers.Main) {
            releaseLastGestures()
            delay(30L.shake(15)) //
            val resp = ServerApi.requestHeartbeat("start")
            if (resp?.code != 0 || resp.data.isNullOrEmpty()) {
                stop("检测失败") // 这个key不能动.不然把之前的停掉了
                val msg = resp?.message ?: ""
                toast((msg).let { it.ifEmpty { "启动检测失败" } } )
                writeToDisk("keyErr", "[start:${resp?.code}:${resp?.message}]")
            } else {
                Keys.setup(resp.data)
                startScriptJob()
                startApiHeartbeat()

                while(isRunning) {
                    if (Keys.isUsingCDKeyInfoActivating != 0) {
                        printLog { "卡密已失效B" }
                        stop("卡密已失效")
                        isInvalid.postValue(true)
                        DiskLogUtils.saveToDisk()
                        repeat(3) {
                            printLog { "卡密已失效B" }
                            toast("卡密已失效")
                        }
                    }
                    delay(1200000L) // 20分看一次
                }
            }
        }
    }

    private fun startScriptJob() {
        jobStart?.cancel()
        jobStart = appScopeLaunch(Dispatchers.Main) {
            delay(800)
            outputText("游戏识别")
            if (Properties.scene.isEmpty()) {
                toast("任务读取失败")
                return@appScopeLaunch
            }
            if (Properties.scene.isNotEmpty()) {
                val out = GComponents.initialize()
                if (out.gc == null) {
                    return@appScopeLaunch
                }
                toast("游戏识别完成 ${Properties.scene}")
                DiskLogUtils.record("游戏识别完成 ${Properties.scene}")
                if (Properties.scene == C.S_JL) {
                    startSceneJL()
                    analysisEvent(C.S_JL)
                } else if (Properties.scene == C.S_SM) {
                    startSceneSM()
                    analysisEvent(C.S_SM)
                } else if (Properties.scene == C.S_HY) {
                    startSceneHY()
                    analysisEvent(C.S_HY)
                } else if (Properties.scene == C.S_JT) {
                    startSceneJT()
                    analysisEvent(C.S_JT)
                } else if (Properties.scene == C.S_RH) {
                    startSceneRH()
                    analysisEvent(C.S_RH)
                } else if (Properties.scene == C.S_FB) {
                    startSceneFB()
                    analysisEvent(C.S_FB)
                } else if (Properties.scene == C.S_HM) {
                    startSceneHM()
                    analysisEvent(C.S_HM)
                } else if (Properties.scene == C.S_FW) {
                    startSceneFW()
                    analysisEvent(C.S_FW)
                } else if (Properties.scene == C.S_YW) {
                    startSceneYW()
                    analysisEvent(C.S_YW)
                } else if (Properties.scene == C.S_FM) {
                    startSceneFM()
                    analysisEvent(C.S_FM)
                } else if (Properties.scene == C.S_MY) {
//                    startSceneMY()
                    analysisEvent(C.S_MY)
                }
            }
//            ControllerLayout.notifyUpdate()
            FloatControllerLayout.notifyUpdate()
            Properties.setControllerRunning(true)
            writeToDisk("hasRunningSuccess", "1")
            clickStartStatus.value = false
            xLastUnknownTs = 0L // 重制

            xScriptStartTs = System.currentTimeMillis()
            val stopTime = Properties.stopCountdown * 1000L
            xScriptEndTs = if (stopTime > 0L) xScriptStartTs + stopTime else 0L
        }
    }

    private fun startApiHeartbeat() {
        jobHeartBeat?.cancel()
        jobHeartBeat = appScopeLaunch(Dispatchers.IO) {
            // 这里随机范围20秒-80秒期间.后面卡掉sessionId不一样的
            delay(50000L.shake(30000)) //
            while(isRunning) {
                val resp = ServerApi.requestHeartbeat("beat")
                if (resp?.code == 0 && resp.data?.isNotEmpty() == true) {
                    Keys.setup(resp.data) // 心跳换成新的token续费
                }
                delay(3000000L) // 50分钟. 看一个月流量消耗
            }
        }
    }

    fun stop(reason: String) = runOnUIThread { // uiThread 大概率 有非UI线程
        printLog { "Controller stop $reason" }
        clickStartStatus.value = false

        jobHeartBeat?.cancel()
        jobStart?.cancel()
        jobCheck?.cancel()
        jobCancel = appScopeLaunch(Dispatchers.Main) {
            running.value = false
            printLog { "Controller clearParser" }
            // Screenshot.get().setParser("") // 先把依赖GComponent的停掉.避免NPE.虽然处理了
            delay(600)

            outputText(reason, Color.RED)
            if (reason != "检测失败") toast(reason)
            SmallMapLayout.release()
 
            stopSceneJL()
            stopSceneHY()
            stopSceneSM()
            stopSceneJT()
            stopSceneFW()
            stopSceneYW()
            stopSceneFM()
            stopSceneHM()
            stopSceneRH()
            stopSceneFB()
//            stopSceneHY2()

            releaseLastGestures()
            FloatControllerLayout.notifyShow(true)

            if (reason != "检测失败") ServerApi.requestHeartbeat("stop") // 服务器同步停止
            Keys.clear() // 清空cdk
            delay(500)
        }
    }

    companion object {
        val clickStartStatus = MutableLiveData<Boolean>()

        val running = MutableLiveData(false)
        val isRunning get() = running.value ?: false
        val isInvalid = MutableLiveData<Boolean?>()
        private var ins: Controller? = null
        fun get(): Controller {
            return ins ?: synchronized(this) { ins ?: Controller().also { ins = it } }
        }

        fun stopIfRunning(reason: String) {
            if (isRunning) get().stop(reason)
        }

        suspend fun processExitGame(output: String) {
            // 将截图保存到本地
            DiskLogUtils.record(output)
            if (output.contains(C.ERROR_END)) {
                CrashUtils.saveErrorStoppedScreenshotToDisk()
                writeToDisk("exitTip", "1") // 回到首页增加标记
            }
            outputText(output)
            errorEndLogout()
            get().stop(output) // 可能已经退出到了登录页面了
        }
    }
}


